#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <signal.h>

#define N 128

#define ERR_LOG(errmsg) do{\
                             perror(errmsg);\
                             exit(1);\
                          }while(0)


//信号回调函数
void handler(int sig){
    wait(NULL);//父进程通过wait()回收僵尸进程。
}

int main(int argc,char const *argv[]){
    if(argc < 3){
        fprintf(stderr,"Usage: %s [ip] [port]\n",argv[0]);
	exit(1);
    }

    //第一步：创建套接字
    int sockfd;
    if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1){
       ERR_LOG("fail to socket");
    }

    //将套接字设置为允许重复使用本机地址(因为没有多台设备充当客户端)或者设置为端口复用
    int on=1;
    if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0){
       ERR_LOG("fail to setsockopt");
    }

    //第二步：将套接字与服务器网络信息结构体绑定
    struct sockaddr_in serveraddr;
    socklen_t addrlen = sizeof(serveraddr);

    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));
    
    if(bind(sockfd,(struct sockaddr *)&serveraddr,addrlen)==-1){
        ERR_LOG("fail to bind");
    }

    //第三步：将套接字设置为被动监听状态
    if(listen(sockfd,10)==-1){
        ERR_LOG("fail to listen");
    }

    //第四步：阻塞等待客户端的连接请求
    int acceptfd;//这个变量用来接收accept()返回的套接字，每个套接字代表与一个客户端的连接
    struct sockaddr_in clientaddr;
  
    //使用信号，异步的方式处理僵尸进程
    signal(SIGCHLD,handler);

    while(1){
        if((acceptfd = accept(sockfd,(struct sockaddr *)&clientaddr,&addrlen))==-1){
            ERR_LOG("fail to accept");
        }
        
        //打印连接的客户端的信息
        printf("ip:%s,port:%d\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));

        pid_t pid=fork();
        if(pid > 0){
            
        }else if(pid == 0){
        //进入子进程，循环接收子进程消息

            char buf[N] = "";
            ssize_t bytes;
            while(1){
                if((bytes = recv(acceptfd,buf,N,0))<0)
                {
                    ERR_LOG("fail to recv");
                }
                else if(bytes == 0)
                {
                    printf("The client quited\n");
                    exit(1);
                }

                if(strncmp(buf,"quit",4)==0)
                {
                    exit(0);
                }

                printf("from client: %s\n",buf);

                strcat(buf,"*_*");//将参数1和参数2拼接，并将结果返回到参数1中。

                if(send(acceptfd,buf,N,0)<0)
                {
                    ERR_LOG("fail to send");
                }
            }
        }
        else if(pid==-1){
            ERR_LOG("fail to create a process!");
        }
    }


    return 0;
}


